using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.ObjectModel;

namespace TST.SharePoint.Shared
{
    /// <summary>
    /// A combination of a strongly typed dictionary and list.
    /// Has both a position indexer and key indexer.
    /// </summary>
    /// <typeparam name="TKey">The type of the key.</typeparam>
    /// <typeparam name="TItem">The type of the item.</typeparam>
    [Serializable]
    public class HashList<TKey, TItem> :
        KeyedCollection<TKey, TItem>,
        IHashList<TKey>
    where TItem : IIdentifiable<TKey>
    {
        public void AddRange(IEnumerable<TItem> enumerable)
        {
            foreach (TItem item in enumerable)
            {
                if (!Contains(item.Id))
                    Add(item);
            }
        }

        /// <summary>
        /// When implemented in a derived class, extracts the key from the specified element.
        /// </summary>
        /// <param name="item">The element from which to extract the key.</param>
        /// <returns>The key for the specified element.</returns>
        protected override TKey GetKeyForItem(TItem item)
        {
            return item.Id;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HashList{TKey, TItem}"/> class.
        /// </summary>
        public HashList()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HashList{TKey, TItem}"/> class.
        /// </summary>
        /// <param name="items">The items.</param>
        public HashList(IEnumerable<TItem> items)
        {
            foreach (TItem item in items)
            {
                Add(item);
            }
        }

        #region ICollection<IIdentifiable<TKey>> Members

        #region IList<IIdentifiable<TKey>> Members

        #region IHashList<TKey,IIdentifiable<TKey>> Members

        IIdentifiable<TKey> IHashList<TKey>.this[TKey key]
        {
            get
            {
                return this[key];
            }
        }

        #endregion

        ///<summary>
        ///Determines the index of a specific item in the <see cref="T:System.Collections.Generic.IList`1"></see>.
        ///</summary>
        ///
        ///<returns>
        ///The index of item if found in the list; otherwise, -1.
        ///</returns>
        ///
        ///<param name="item">The object to locate in the <see cref="T:System.Collections.Generic.IList`1"></see>.</param>
        int IList<IIdentifiable<TKey>>.IndexOf(IIdentifiable<TKey> item)
        {
            return IndexOf((TItem)item);
        }

        ///<summary>
        ///Inserts an item to the <see cref="T:System.Collections.Generic.IList`1"></see> at the specified index.
        ///</summary>
        ///
        ///<param name="item">The object to insert into the <see cref="T:System.Collections.Generic.IList`1"></see>.</param>
        ///<param name="index">The zero-based index at which item should be inserted.</param>
        ///<exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.IList`1"></see> is read-only.</exception>
        ///<exception cref="T:System.ArgumentOutOfRangeException">index is not a valid index in the <see cref="T:System.Collections.Generic.IList`1"></see>.</exception>
        void IList<IIdentifiable<TKey>>.Insert(int index, IIdentifiable<TKey> item)
        {
            Insert(index, (TItem)item);
        }

        ///<summary>
        ///Gets or sets the element at the specified index.
        ///</summary>
        ///
        ///<returns>
        ///The element at the specified index.
        ///</returns>
        ///
        ///<param name="index">The zero-based index of the element to get or set.</param>
        ///<exception cref="T:System.ArgumentOutOfRangeException">index is not a valid index in the <see cref="T:System.Collections.Generic.IList`1"></see>.</exception>
        ///<exception cref="T:System.NotSupportedException">The property is set and the <see cref="T:System.Collections.Generic.IList`1"></see> is read-only.</exception>
        IIdentifiable<TKey> IList<IIdentifiable<TKey>>.this[int index]
        {
            get
            {
                return this[index];
            }
            set
            {
                this[index] = (TItem)value;
            }
        }

        #endregion

        ///<summary>
        ///Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1"></see>.
        ///</summary>
        ///
        ///<param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1"></see>.</param>
        ///<exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only.</exception>
        void ICollection<IIdentifiable<TKey>>.Add(IIdentifiable<TKey> item)
        {
            Add((TItem)item);
        }

        ///<summary>
        ///Determines whether the <see cref="T:System.Collections.Generic.ICollection`1"></see> contains a specific value.
        ///</summary>
        ///
        ///<returns>
        ///true if item is found in the <see cref="T:System.Collections.Generic.ICollection`1"></see>; otherwise, false.
        ///</returns>
        ///
        ///<param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1"></see>.</param>
        bool ICollection<IIdentifiable<TKey>>.Contains(IIdentifiable<TKey> item)
        {
            return Contains((TItem)item);
        }

        ///<summary>
        ///Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1"></see> to an <see cref="T:System.Array"></see>, starting at a particular <see cref="T:System.Array"></see> index.
        ///</summary>
        ///
        ///<param name="array">The one-dimensional <see cref="T:System.Array"></see> that is the destination of the elements copied from <see cref="T:System.Collections.Generic.ICollection`1"></see>. The <see cref="T:System.Array"></see> must have zero-based indexing.</param>
        ///<param name="arrayIndex">The zero-based index in array at which copying begins.</param>
        ///<exception cref="T:System.ArgumentOutOfRangeException">arrayIndex is less than 0.</exception>
        ///<exception cref="T:System.ArgumentNullException">array is null.</exception>
        ///<exception cref="T:System.ArgumentException">array is multidimensional.-or-arrayIndex is equal to or greater than the length of array.-or-The number of elements in the source <see cref="T:System.Collections.Generic.ICollection`1"></see> is greater than the available space from arrayIndex to the end of the destination array.-or-Type T cannot be cast automatically to the type of the destination array.</exception>
        void ICollection<IIdentifiable<TKey>>.CopyTo(IIdentifiable<TKey>[] array, int arrayIndex)
        {
            throw new NotImplementedException();
        }

        ///<summary>
        ///Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"></see>.
        ///</summary>
        ///
        ///<returns>
        ///true if item was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"></see>; otherwise, false. This method also returns false if item is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"></see>.
        ///</returns>
        ///
        ///<param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"></see>.</param>
        ///<exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only.</exception>
        bool ICollection<IIdentifiable<TKey>>.Remove(IIdentifiable<TKey> item)
        {
            return Remove((TItem)item);
        }

        ///<summary>
        ///Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only.
        ///</summary>
        ///
        ///<returns>
        ///true if the <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only; otherwise, false.
        ///</returns>
        ///
        bool ICollection<IIdentifiable<TKey>>.IsReadOnly
        {
            get
            {
                return ((ICollection<TItem>)this).IsReadOnly;
            }
        }

        #endregion

        ///<summary>
        ///Returns an enumerator that iterates through the collection.
        ///</summary>
        ///
        ///<returns>
        ///A <see cref="T:System.Collections.Generic.IEnumerator`1"></see> that can be used to iterate through the collection.
        ///</returns>
        ///<filterpriority>1</filterpriority>
        IEnumerator<IIdentifiable<TKey>> IEnumerable<IIdentifiable<TKey>>.GetEnumerator()
        {
            return new Enumerator<IIdentifiable<TKey>>(GetEnumerator());
        }

        #region IEnumerable Members

        public System.Collections.IEnumerable Iterate()
        {
            return this;
        }

        #endregion

    }

    public interface IHashList<TKey> : IList<IIdentifiable<TKey>>
    {
        bool Contains(TKey key);
        IIdentifiable<TKey> this[TKey key] { get; }
    }
}
